package com.binlog.util;

import cn.hutool.core.io.IoUtil;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.binlog.config.BinLogDbProperty;
import com.binlog.config.BinLogDbPropertys;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Bs data source
 *
 * @description:
 * @author: chenyj
 * @date 2023 -11-13 15:10:37
 */
@Slf4j
public class DruidDataSource {

    private static final String DRIVER_ADDRESS = "jdbc:mysql://%s:%s/%s?serverTimezone=Asia/Shanghai";

    private static Map<String, DataSource> dataSourceMap = new HashMap<>();

    /**
     * Init datesource. 初始化连接池
     *
     * @param binLogDbPropertys the bin log db propertys
     */
    public static void initDatesource(BinLogDbPropertys binLogDbPropertys) {
        if (null != binLogDbPropertys.getBackupsDatasource()) {
            addDataSource(binLogDbPropertys.getBackupsDatasource(), binLogDbPropertys.getBackupsDatasource().getDataSourceName(),
                    binLogDbPropertys.getBackupsDatasource().getSchema());
            binLogDbPropertys.getDatasource().stream().forEach(dataSource -> addDataSource(
                    binLogDbPropertys.getBackupsDatasource(),
                    binLogDbPropertys.getBackupsDatasource().getDataSourceName() + "_" + dataSource.getSchema(), dataSource.getSchema()));
        }
        binLogDbPropertys.getDatasource().stream().forEach(dataSource -> addDataSource(dataSource,
                dataSource.getDataSourceName(), dataSource.getSchema()));
        log.info("初始化数据源成功:{}", dataSourceMap.size());
    }

    @SneakyThrows
    private static void addDataSource(BinLogDbProperty conf, String name, String schema) {
        //获取Properties对象，加载到该对象中
        Properties properties = new Properties();
        properties.setProperty("url", String.format(DRIVER_ADDRESS, conf.getHost(), conf.getPort(), schema));
        properties.setProperty("username", conf.getUsername());
        properties.setProperty("password", conf.getPassword());
        properties.setProperty("driverClassName", "com.mysql.cj.jdbc.Driver");
        properties.setProperty("initialSize", "10");
        properties.setProperty("maxActive", "50");
        properties.setProperty("maxWait", "2000");

        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        dataSourceMap.put(name, dataSource);

    }

    /**
     * Gets conn.获取数据库连接
     *
     * @param dataSource the data source
     * @return the conn
     */
    @SneakyThrows
    public static Connection getConn(String dataSource) {
        return dataSourceMap.get(dataSource).getConnection();
    }


    /**
     * 关闭连接
     * 查询调用这个方法
     *
     * @param connection the connection
     * @param statement  the statement
     * @param resultSet  the result set
     */
    @SneakyThrows
    public static void close(Connection connection, Statement statement, ResultSet resultSet) {
        IoUtil.close(resultSet);
        IoUtil.close(statement);
        IoUtil.close(connection);
    }

    /**
     * 关闭连接
     * 增删改没有结果集
     *
     * @param connection the connection
     * @param statement  the statement
     */
    public static void close(Connection connection, Statement statement) {
        //直接调用上面的方法
        close(connection, statement, null);
    }

    /**
     * 通用的增删改方法
     *
     * @param dataSource the data source
     * @param sql        the sql
     * @param args       the args
     */
    public static void execute(String dataSource, String sql, Object... args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            //获取连接
            conn = getConn(dataSource);
            //获取预编译对象
            ps = conn.prepareStatement(sql);
            //获取元数据，得到有多少占位符
            ParameterMetaData metaData = ps.getParameterMetaData();
            int count = metaData.getParameterCount();
            //循环获取赋值
            for (int i = 0; i < count; i++) {
                ps.setObject(i + 1, args[i]);
            }
            //执行SQL语句
            ps.execute();
        } catch (Exception e) {
            log.info("执行execute方法失败:{}", e.getMessage());
        } finally {
            close(conn, ps);
        }
    }

}
